Android系统从4.1(API 16)开始加入Choreographer这个类来控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。
Choreographer 即编舞者 负责协调app端的图形绘制,这里主要是等待vsync信号。垂直信号到来后就要开始准备绘制下一帧的数据。Choreographer主要是在ViewRootImpl中使用的,ViewRootImpl是view树的管理者,负责view树的逻辑处理及事件事件输入。
所有的绘制流程是从ViewRootImpl.java的scheduleTraversals开始的,这个方法会去请求vsync信号,并在信号到来时去绘制更新ui。
1 | void scheduleTraversals() { |
这里我们不关心具体的绘制过程,主要是看Choreographer如何通过vysnc信号来协调界面的绘制。
这里首先是通过Choreographer对象mChoreographer对象post了一个回调,告诉Choreographer当vsync信号到达时帮我调用mTraversalRunable回调。这个回调的定义如下:
1 | final class TraversalRunnable implements Runnable { |
doTraversal()内部会调用performTraversals()方法,从而开启view绘制的三大流程。
下面我们看看Choreographer是如何将vsync接受信号并告之ViewRoomImpl的刷新回调的。我们就从这个postCallback入手分析。
1 | public void postCallback(int callbackType, Runnable action, Object token) { |
postCallback最终会调用postCallbackDelayedInternal,参数delayMillis为0,所以会调用scheduleFrameLocked进一步进行操作。这里需要注意的是每次请求都会添加到其类型对应的回调队列中, 这里的mCallbackQueues是一个根据类型区分的回调队列,有四种类型,分别是输入回调,动画回调和绘制回调以及Choreographer.CALLBACK_COMMIT。
1 | private void scheduleFrameLocked(long now) { |
接着调用doScheduleVsync
1 | void doScheduleVsync() { |
1 | private void scheduleVsyncLocked() { |
这里的mDisplayEventReceiver是一个FrameDisplayEventReceiver对象,它继承了DisplayEventReceiver,其中实现了其方法onVsync,这个方法就是当onVsync信号到达时的回调方法。
1 | //垂直信号到达 |
垂直信号vsync到来后会触发doFrame,在这个方法里面会进行我们的回调,即mTraversalRunnable。
1 | void doFrame(long frameTimeNanos, int frame) { |
1 | void doCallbacks(int callbackType, long frameTimeNanos) { |
这里会根据类型从队列中取出相应的回调进行调用。这个就是上层对于vsync的处理。接下来我们看看底层的vsync信号是如何传递给Choreographer的。这就需要看看FrameDisplayEventReceiver的父类DisplayEventReceiver,这个DisplayEventReceiver会通过native层进行初始化,native层通过它的成员方法dispatchVsync将vsync信号报告给上层,即调用onVsync。我们先看其构造方法:
1 | public DisplayEventReceiver(Looper looper, int vsyncSource) { |
其构造方法是调用nativeInit进行初始化的,并将当前对象this作为一个接收器传递给底层。我们看看这个方法
1 | frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp |
在初始化方法中会创建本地的接受器对象,它同时是用我们传递的java层的接受器对象构造的,构造完成后即进行初始化。这个NativeDisplayEventReceiver的定义如下
1 | //native的显示事件接受器 |
NativeDisplayEventReceiver继承自DisplayEventDispatcher,而DisplayEventDispatcher又实现了LooperCallback的接口handleEvent,这个方法是Looper的事件回调,也就是当Looper接受到消息后会对其进行调用,那么这个DisplayEventDispatcher就是负责进行消息事件的转发的。后面我们看看它是如何将vsync信号转发的。
1 | //初始化receiver |
这里的addFd将其注册到为Looper的事件回调,注意这里第四个参数this,因为DisplayEventDispatcher是继承LooperCallback的。这样当事件到来后会调用handleEvent。
还有,在NativeDisplayEventReceiver的内部持有一个DisplayEventReceiver对象,这个对象比较重要,它是负责和Sf打交道的。
1 | DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { |
1 | sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( |
刚刚说到DisplayEventReceiver是和Sf打交道的, 它和NativeDisplayEventReceiver是同时创建的。在其构造方法中首先通过sf的createDisplayEventConnection创建一个连接对象即Connection对象,mEventThread是管理绘图延时对象的vsync信号处理,它是一个EventThread。Connection就是由它创建的。
1 | sp<EventThread::Connection> EventThread::createEventConnection() const { |
这个connection在第一次被引用时会进行注册,即将其添加到mEventThread的连接队列,在vsync消息到来时调用其postEvent方法对事件进行转发。
1 | void EventThread::Connection::onFirstRef() { |
postEvent实际上会调用DisplayEventReceiver的setEvents方法,这样会触发getEvents的回调从而通过DisplayEventDispatcher的handleEvent回调方法。
1 | ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel, |
最终调用NativeDisplayEventReceiver的dispatchVsync回调给java层的dispatchVsync,这样vsync信号就传递给上层应用了。